#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netdb.h>
#include <sys/file.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>

#ifdef MAC_OS_X
#include <malloc/malloc.h>
#else
#include <malloc.h>
#endif

#include <errno.h>

#include "libsnet.h"

/***
 * Closes Network Socket
 */
#define net_close(x) close(x)

/**
 * Wrapper Function for accept()
 */
int net_open_connection(int _socket, struct sockaddr_in* _addr)
{
	extern char* inet_ntoa();
	int _new_socket;
	int _addr_len;
	
	_addr_len = sizeof(struct sockaddr);
	_new_socket = accept(_socket, (void*)_addr, &_addr_len);

	if((_new_socket == -1) && (errno == EWOULDBLOCK))
		return ACCEPT_FAILURE;

	return _new_socket;
}

/***
 * Open Server Socket
 */

 int net_open_server_socket(int port)
 {
 	int _socket;
	int _option = 1;
	struct sockaddr_in _server;

	_server.sin_family = AF_INET;
	_server.sin_addr.s_addr = INADDR_ANY;
	_server.sin_port = htons(port);

	if((_socket = socket(AF_INET, SOCK_STREAM,0)) < 0)
		return SOCKET_FAILURE;

	if(setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR, (char*) &_option, sizeof(_option)) < 0)
		return SET_SOCKET_OPT_FAILURE;
	
	if(bind(_socket, (struct sockaddr *)&_server, sizeof(_server)))
		return BIND_FAILURE;

	if(listen(_socket, 5) < 0)
		return LISTEN_FAILURE;
 	return _socket;
 }

/***
 * Connect to Server
 */

int net_connect(char* srv_addr, int port)
{
	struct sockaddr_in _server;
	int _connect_fd = 0;

	/* structure initialize */
	bzero((char*) &_server, sizeof(_server));
	_server.sin_family = PF_INET;
	_server.sin_port = htons(port);
	_server.sin_addr.s_addr = inet_addr(srv_addr);

	if((_connect_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		return SOCKET_FAILURE;

	if(connect(_connect_fd, (struct sockaddr*)&_server, sizeof(_server) ) < 0)
	{
		close(_connect_fd);
		return CONNECTION_FAILURE;
	}

	return _connect_fd;
}

/***
 * Send Function
 */

int net_send(int _socket, packet_header* pack_header, void* pack_body)
{
 	size_t _buf_size = sizeof(packet_header) + pack_header->body_size;
	unsigned char* _buf = (unsigned char*)malloc(_buf_size);
	int _send_byte = 0;

	memcpy(_buf, pack_header, sizeof(packet_header));

	if(pack_header->body_size > 0)
		memcpy(_buf+sizeof(packet_header), pack_body,pack_header->body_size); 

	printf("Send To %d, PacketSize: %d\n", _socket, _buf_size);

 	if((_send_byte = send(_socket, _buf,_buf_size, 0)) < 0)
	{
		return SEND_FAILURE;
	}

	free(_buf);
 	return _send_byte;
}

/***
 * Receive Function
 */
unsigned char recv_buf[RECV_BUFF];
int header_parsed 			= net_false;
int body_parsed 			= net_false;
int recv_buffer_position 	= 0;
int parsed_buffer_position 	= 0;

void reset_recv_variables()
{
	header_parsed = net_false;
	body_parsed = net_false;
	recv_buffer_position = 0;
	parsed_buffer_position = 0;
}

int net_recv(int _socket, packet_header* pack_header, unsigned char* pack_body)
{
	int _recv_byte = 0;
	unsigned char* _recv_body;

	/* Initial Receiving*/
	if((_recv_byte = recv(_socket, recv_buf, RECV_BUFF, 0)) <= 0)	
		return RECV_FAILURE;

	recv_buffer_position = _recv_byte;
	printf("recv_byte: %d\n", _recv_byte);

	/* Parse Packet Header*/
	while(header_parsed != net_true)
	{
		if(recv_buffer_position >= sizeof(packet_header))
		{
			memcpy(pack_header, recv_buf, sizeof(packet_header));
			parsed_buffer_position = sizeof(packet_header);
			header_parsed = net_true;
			break;
		}
		else
		{
			/* TODO: Multiple Recv Functionality? */
			if(_recv_byte = recv(_socket, recv_buf+recv_buffer_position, 
									      RECV_BUFF-recv_buffer_position, 0) 
										  < 0)	
				return RECV_FAILURE;
			else
				recv_buffer_position += _recv_byte;
		}

		printf("recv_buffer_pos %d\n", recv_buffer_position);
	}

	/* Packet == Header */
	if(pack_header->body_size == 0)
	{
		printf("Header Packet Received\n");
		reset_recv_variables();
		return net_true;
	}

	printf("Header Parsed\n");
	
	/* Parse Packet Body */
	while(body_parsed != net_true)
	{
		if(recv_buffer_position == (pack_header->body_size + sizeof(packet_header)))
		{
			printf("Packet Body Size %d\n", pack_header->body_size);
			printf("Header Size %d\n", sizeof(packet_header));
			memcpy((unsigned char*)pack_body, ((unsigned char*)recv_buf) + sizeof(packet_header), 
							  					pack_header->body_size);
			/* Memory Leak may occur by cbchoi*/
			body_parsed = net_true;
			break;
		}
		else if(recv_buffer_position > pack_header->body_size + sizeof(packet_header))
		{
			printf("Something ackward packet received!\n");
			printf("body size:%d, buf_position:%d\n", pack_header->body_size, recv_buffer_position);
			return RECV_FAILURE_BODYSIZE;
		}
		else 
		{
			if(_recv_byte = recv(_socket, recv_buf+recv_buffer_position, 
									      RECV_BUFF-recv_buffer_position, 0) 
										  < 0)	
				return RECV_FAILURE;
			else
				recv_buffer_position += _recv_byte;
		}
	}

	reset_recv_variables();
	return net_true;
}
